//
//  BBDispatchViewDelegate.m
//  BBTUIOTest
//
//  Created by ben smith on 8/20/08.
//  This file is part of BBTUIOTest.
//
//  BBTUIOTest is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.

//  BBTUIOTest is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.

//  You should have received a copy of the GNU Lesser General Public License
//  along with BBTUIOTest.  If not, see <http://www.gnu.org/licenses/>.
// 
//  Copyright 2008 Ben Britten Smith ben@benbritten.com .
//
//

#import "BBDispatchViewDelegate.h"
#import "BBTestAppView.h"
#import "BBOSC.h"
#import "BBTUIOCursor.h"


@implementation BBDispatchViewDelegate

@synthesize mainView;
@synthesize mouseFaker;

- (id) init
{
	self = [super init];
	if (self != nil) {
		// setup my storage
		activeBlobs = [[NSMutableDictionary alloc] init];
	}
	return self;
}

// this is called by the OSCListener with the raw packet data
-(void)dispatchRawPacket:(NSData*)someData
{
	// all we really care about right now are TUIO/2DCur messages
	// these come in bundles, so any non-bundle will be ignored
	id decodedPacket = [BBOSCDecoder decodeData:someData];
	if (![decodedPacket isKindOfClass:[BBOSCBundle class]]) return;
	
	// decompress the bundle into it's contained messages, 
	// dispatch each one individually

	for (BBOSCMessage * message in [decodedPacket attachedObjects]) {
		[self dispatchMessage:message];
	}
}

// this is a specific implementation designed for the TUIO/2DCur messages
// the come in four types: (and in this order, generally)

// source - defines where the packets are coming from
// set - this adds new 'events', could be old blob ids or new ones
// alive - this tells us which blobs (including the ones just set) that are still alive
// fseq - this signals the end of this frame, and gives a sequence number, in theory the frames can come out of order, and we should be looking for that, but not for this app

-(void)dispatchMessage:(BBOSCMessage*)message
{
	// need to figure out what kind of message it is
	// lowercase all the strings to make it easier to match up
	NSString * addy = [[[message address] address] lowercaseString];

	// for now, just check to make sure it is what we think it is
	// not a TUIO/2DCur? then ignore it
	if (![addy isEqualToString:@"/tuio/2dcur"]) return;
	// now for the 'command'
	NSString * command = [[[[message attachedObjects] objectAtIndex:0] stringValue] lowercaseString];
	
	// a bit of manual hackery. this could be nicer
	if ([command isEqualToString:@"fseq"]) {
		[self processFseq:[message attachedObjects]];
		return;
	}
	if ([command isEqualToString:@"alive"]) {
		[self processAlive:[message attachedObjects]];
		return;
	}
	if ([command isEqualToString:@"source"]) {
//		[self processSource:[message attachedObjects]];
		return;
	}
	if ([command isEqualToString:@"set"]) {
		[self processSet:[message attachedObjects]];
		return;
	}
}

// just a nice quick way to grab the identified cursors out of a 
// dictionary
-(NSString*)keyForID:(NSInteger)num
{
	// just the ID in string form
	return [NSString stringWithFormat:@"%d",num];
}

// the set either adds a cursor or updates an existing cursor
// if we use the id as a key, then we just need to 
// set the object in a dictionary and if the key already exists
// then it will overwrite, otherwise it will just add it
// NOTE: if we cared about 'blobDown' style events we would want to 
// check to see if the key is already in the dictionary, but for now we dont
-(void)processSet:(NSArray*)args
{
	// make a enw cursor from the arg array
	BBTUIOCursor * cursor = [[BBTUIOCursor alloc] initWithArgs:args];
	// put it into the acitve blob collection
	if ([activeBlobs objectForKey:[self keyForID:[cursor cursorID]]] == nil) {
		// then it is a new blob
		cursor.isNew = YES;
	} else {
		// no longer new, now old and boring
		cursor.isNew = NO;
	}
	[activeBlobs setObject:cursor forKey:[self keyForID:[cursor cursorID]]];
}

// this tells us which blobs are still on the board
// we need to check against all our active blobs and remove any that
// are not in the alive message
// NOTE: this is where you would synthesize blobUp style event, if you wanted to
-(void)processAlive:(NSArray*)args
{
	// we are going to put all the still breathing blobs into
	// a temporary storage, then clear the active blobs
	// then put the still breathing ones back into the active list
	NSMutableDictionary * blobStorage = [NSMutableDictionary dictionary];
	NSString * key; 
	int i;
	// ignore the first arg, it is the 'set' string
	for (i = 1; i < [args count]; i++) {
		// get the key from the arg list
		key = [self keyForID:[[args objectAtIndex:i] intValue]]; 
		// grab the cursor out of the active list and add it to the storage
		id cursor = [activeBlobs objectForKey:key];
		if (cursor != nil)
			[blobStorage setObject:cursor forKey:key];
	}
	// any blobs left in the storage get obliterated here
	[activeBlobs removeAllObjects];
	// move all the still breathing blobs back into storage
	[activeBlobs addEntriesFromDictionary:blobStorage];
}

// this is the frame sequence.  in theory we should be checking to make sure that 
// all the events arrive at the proper time. but since this is visual control
// instead of sound, we will blow it off for now
// however, we will use it as an 'end of frame' event
-(void)processFseq:(NSArray*)args
{
	// dont really care about the sequence right now
	// so just set the view blobs and that is all
	[mainView setCursors:[activeBlobs allValues]];
	[mainView blobCycleDone];
	[mouseFaker setCursors:[activeBlobs allValues]];
}

// ehh.. we dont really care about the source.  maybe someday, but not today
-(void)processSource:(NSArray*)args
{
	// dont really care about the source right now
	
}

- (void) dealloc
{
	[mainView release];
	[activeBlobs release];
	[super dealloc];
}


@end
